package org.mebusw.davincicode;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

import org.mebusw.dao.IGameTableDAO;
import org.mebusw.dao.IUserDAO;
import org.mebusw.dao.factory.DAOFactory;
import org.mebusw.davincicode.vo.DavinciCard;
import org.mebusw.davincicode.vo.DavinciGameTable;
import org.mebusw.davincicode.vo.DavinciPlayer;
import org.mebusw.game.IGameManager;
import org.mebusw.vo.GameTable;
import org.mebusw.vo.Player;
import org.mebusw.vo.User;

public class GameManager implements IGameManager {
	private DavinciGameTable gt;
	private PlayerManager[] playerManager;
	private CardPoolManager cardPoolManager;

	// private final static int MAX_PLAYER = 4;

	public GameManager() {
		cardPoolManager = new CardPoolManager();
	}

	public void initNewGame() {

		DavinciCard card;
		System.out.println("Davinci GameManager.initNewGame():"
				+ gt.getPlayerCount());

		gt.setPool(new ArrayList<DavinciCard>());
		cardPoolManager.setCards(gt.getPool());
		cardPoolManager.initPool(true);

		for (int i = 0; i < gt.getPlayerCount(); i++) {
			for (int j = 0; j < DavinciGameTable.getINIT_SET()[gt
					.getPlayerCount()]; j++) {
				card = cardPoolManager.randTake();
				card.setOwner(i);
				playerManager[i].insertSort(card);
			}
		}

		Date t = new Date();
		Random rand = new Random(t.getTime());
		int startPlayer = rand.nextInt(gt.getPlayerCount());
		playerManager[startPlayer].getPlayer().setState(PlayerState.TAKING);
		gt.setCurrPlayer(startPlayer);

		gt.setState(GameStatus.ONGOING);

	}

	public void joinGame(String playerName, int uid) {
		System.out
				.println("Davinci-GameManager.joinGame():" + uid + playerName);
		int i;
		for (i = 0; i < gt.getPlayerCount(); i++) {
			if (playerManager[i].getPlayer().getName().equals(playerName)) {
				return;
			}
			if (playerManager[i].getPlayer().getName().equals("")) {
				playerManager[i].getPlayer().setName(playerName);
				playerManager[i].getPlayer().setUserId(uid);
				break;
			}
		}
		if (i == gt.getPlayerCount() - 1) {
			// that means player slots are full.
			initNewGame();
		}
	}

	public int playerNameToID(String username) {
		for (int i = 0; i < playerManager.length; i++) {
			if (playerManager[i].getPlayer().getName().equals(username))
				return i;
		}
		return DavinciGameTable.UNKNOWN_PLAYER;

	}

	public void loadGame(int gid) {
		IGameTableDAO dao = DAOFactory.getIGameTableDAOInstance();
		try {
			System.out.println("loading game gid=" + gid);
			gt = (DavinciGameTable) dao.findById(gid);
			if (gt != null) {
				cardPoolManager.setCards(gt.getPool());
				playerManager = new PlayerManager[gt.getPlayers().size()];
				for (int i = 0; i < gt.getPlayers().size(); i++)
					playerManager[i] = new PlayerManager(gt.getPlayers().get(i));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void createGame(String playerName, int uid, int playerCount,
			String description, String gameName, int expansion) {
		gt = new DavinciGameTable();
		gt.setGameName("Davinci Code");
		System.out.println("GameManager.create():" + playerName + uid
				+ playerCount + description);

		gt.setPlayerCount(playerCount);
		gt.setDescription(description);

		gt.setHands(new ArrayList<List<DavinciCard>>());
		gt.setPlayers(new ArrayList<Player>());

		playerManager = new PlayerManager[gt.getPlayerCount()];
		for (int i = 0; i < gt.getPlayerCount(); i++) {
			ArrayList<DavinciCard> hand = new ArrayList<DavinciCard>();
			gt.getHands().add(hand);
			DavinciPlayer p = new DavinciPlayer("", hand);
			p.setState(PlayerState.WAITING);
			gt.getPlayers().add(p);

			playerManager[i] = new PlayerManager(p);
		}

		gt.setCurrPlayer(0);
		gt.setState(GameStatus.NOTSTARTED);

		IGameTableDAO dao = DAOFactory.getIGameTableDAOInstance();
		try {
			System.out.println("creating game");
			dao.doCreate(gt);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void saveGame() {
		IGameTableDAO dao = DAOFactory.getIGameTableDAOInstance();
		try {
			System.out.println("saving game gid=" + gt.getGid());
			dao.doUpdate(gt);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public GameTable getGt() {
		return gt;
	}

	public void setGt(GameTable gt) {
		this.gt = (DavinciGameTable) gt;
	}

	public void setPlayerManager(PlayerManager[] playerManager) {
		this.playerManager = playerManager;
	}

	public PlayerManager[] getPlayerManager() {
		return playerManager;
	}

	public void setCardPoolManager(CardPoolManager cardPoolManager) {
		this.cardPoolManager = cardPoolManager;
	}

	public CardPoolManager getCardPoolManager() {
		return cardPoolManager;
	}

	/**
	 * When card pool is empty AND all other players' card are revealed, the
	 * only player left is the winner, game over.
	 * 
	 * @return the player ID of the winner
	 */
	private int checkWin(int playerId) {

		int theLeftPlayer = -1;
		int leftPlayerCount = 0;
		if (0 == playerManager[playerId].getStandingCardCount()) {
			/** if all cards of that player are flip down, then he loses. */
			playerManager[playerId].getPlayer().setState(PlayerState.GAMEOVER);
		}
		for (int i = 0; i < getPlayerCount(); i++) {
			if (playerManager[i].getPlayer().getState() != PlayerState.GAMEOVER) {
				theLeftPlayer = i;
				leftPlayerCount++;
			}
		}

		// the only one left is the winner.
		if (leftPlayerCount == 1) {
			playerManager[theLeftPlayer].getPlayer().setState(
					PlayerState.GAMEOVER);
			System.out.println("player #" + theLeftPlayer + " Wins !");
			gt.setState(GameStatus.GAMEOVER);
			updateStatistics(leftPlayerCount);
		} else {
			gt.setState(GameStatus.ONGOING);
			System.out.println("Nobody wins yet.");
		}
		return theLeftPlayer;
	}

	private void updateStatistics(int winner) {
		User user;
		IUserDAO dao;

		System.out.println("updating Statistics gid=" + gt.getGid());
		for (int i = 0; i < gt.getPlayers().size(); i++) {
			dao = DAOFactory.getIUserDAOInstance();
			try {
				user = dao.findByName(gt.getPlayers().get(i).getName());
				user.setPlayed_game_davinci(user.getPlayed_game_davinci() + 1);
				if (winner == i)
					user.setWin_game_davinci(user.getWin_game_davinci() + 1);
				dao = DAOFactory.getIUserDAOInstance();
				try {
					dao.doUpdate(user);
				} catch (Exception e) {
					e.printStackTrace();
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public void onFinish(int newPos) {
		int nextPlayer;
		int nextState;

		if (-1 == newPos) {
			// the last card is not joker
			playerManager[gt.getCurrPlayer()].reinsertSortLast();
		} else {
			// manually insert the joker
			playerManager[gt.getCurrPlayer()].reinsertJokerLast(newPos);
		}

		playerManager[gt.getCurrPlayer()].getPlayer().setState(
				PlayerState.WAITING);

		nextPlayer = nextPlayer();

		gt.setCurrPlayer(nextPlayer);

		/** if no card in pool, next player skips the taking phase */
		nextState = (cardPoolManager.size() == 0) ? PlayerState.FIRSTGUESSING
				: PlayerState.TAKING;
		playerManager[nextPlayer].getPlayer().setState(nextState);
	}

	private int nextPlayer() {
		int nextPlayer;
		do {
			nextPlayer = (gt.getCurrPlayer() + 1) % getPlayerCount();
		} while (playerManager[nextPlayer].getPlayer().getState() != PlayerState.WAITING);
		return nextPlayer;
	}

	public void onTake() {
		DavinciCard card;

		System.out.println("onTake");
		card = cardPoolManager.randTake();
		if (card != null) {
			card.setOwner(gt.getCurrPlayer());
			playerManager[gt.getCurrPlayer()].insertLast(card);
		}
		playerManager[gt.getCurrPlayer()].getPlayer().setState(
				PlayerState.FIRSTGUESSING);
	}

	public void onGuess(int cardId, int target) {
		DavinciCard card = null;
		System.out.println("onGuess " + cardId + target);

		for (int i = 0; i < gt.getPlayers().size() && card == null; i++) {
			for (int j = 0; j < playerManager[i].getHandSize(); j++) {
				card = playerManager[i].getCard(j);
				if (cardId == card.getCardId()) {

					if (target == card.getValue()) {
						card.setRevealed(true);
						playerManager[gt.getCurrPlayer()].getPlayer().setState(
								PlayerState.MOREGUESSING);
						System.out.println("Bingo: " + cardId + target);
						checkWin(i);
					} else {
						System.out.println("Wrong: " + cardId + target);
						if (playerManager[gt.getCurrPlayer()].getLast()
								.isJoker())
							playerManager[gt.getCurrPlayer()].getPlayer()
									.setState(PlayerState.INSERTING);
						else
							onFinish(-1);

						checkWin(gt.getCurrPlayer());
						playerManager[gt.getCurrPlayer()].getLast()
								.setRevealed(true);
					}
					break;
				} else
					card = null;
			}
		}
	}

	private int getPlayerCount() {
		return playerManager.length;
	}

	public GameTable getGtVO() {
		return this.getGt();
	}

}
